import * as Cesium from 'cesium';
function arrayMaxValue(array, comparator = (a, b) => a < b) {
  if (!Array.isArray(array) || array.length === 0) {
    return 0;
  }
  let max = Number.MIN_VALUE;
  for (let i = 0, length = array.length; i < length; i++) {
    if (comparator(max, array[i])) {
      max = array[i];
    }
  }
  return max;
}
function arrayMinValue(array, comparator = (a, b) => a > b) {
  if (!Array.isArray(array) || array.length === 0) {
    return 0;
  }
  let min = Number.MAX_VALUE;
  for (let i = 0, length = array.length; i < length; i++) {
    if (comparator(min, array[i])) {
      min = array[i];
    }
  }
  return min;
}
function ContourLine(contourValue, positionsList) {
  this.contouerValue = contourValue;
  this.positionsList = positionsList;
}
ContourLine.defaulltOptions = {
  color: Cesium.Color.RED,
  width: 1.5
};
const defaultOptions = Object.freeze({
  color: Cesium.Color.RED,
  spacing: 120,
  width: 1.5,
  granularity: Math.PI / Math.pow(2, 11) / 64,
  /// 64.0,
  showTINs: false
});
ContourLine.defaultAppearance = new Cesium.PolylineColorAppearance({
  translucent: false
});
ContourLine.prototype.createGroundPolylinePrimitive = function(appearance, options = { color: Cesium.Color.RED, width: 1.5 }) {
  appearance = Cesium.defaultValue(appearance, ContourLine.defaultAppearance);
  options = {
    ...defaultOptions,
    ...options || {}
  };
  const positionList = this.positionsList;
  const listSize = positionList.length;
  const geometryInstances = [];
  for (let i = 0; i < listSize; i++) {
    const positions = positionList[i];
    const geometry = new Cesium.GroundPolylineGeometry({
      positions,
      //tinItem.vertexes.map((vertex) => vertex.position),
      // loop: true,//无需闭合
      width: options.width
    });
    const instance = new Cesium.GeometryInstance({
      geometry,
      attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(
          // Cesium.Color.fromRandom()
          Cesium.Color.RED.withAlpha(0.75)
        )
      }
    });
    geometryInstances.push(instance);
  }
  return new Cesium.GroundPolylinePrimitive({
    geometryInstances,
    appearance
  });
};
function WorkerWrapper(options) {
  return new Worker(
    "" + new URL("assets/ContourLineWorker_1-rJ-mp8xM.js", import.meta.url).href,
    {
      name: options == null ? void 0 : options.name
    }
  );
}
const defaultColor = Cesium.Color.WHITE;
const defaultWidth = 2;
const defaultSpacing = 100;
const defaultGranularity = Math.PI / Math.pow(2, 11) / 32;
function ContourLineAlgorithm(viewer, options) {
  options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);
  this._viewer = viewer;
  this._spacing = Cesium.defaultValue(options.spacing, defaultSpacing);
  this._width = Cesium.defaultValue(options.width, defaultWidth);
  this._color = Cesium.defaultValue(options.color, defaultColor);
  this._granularity = Cesium.defaultValue(
    options.granularity,
    defaultGranularity
  );
  this._worker = null;
}
Object.defineProperties(ContourLineAlgorithm.prototype, {
  viewer: {
    get() {
      return this._viewer;
    }
  },
  spacing: {
    get: function() {
      return this._spacing;
    },
    set: function(value) {
      if (Cesium.defined(value) && typeof value === "number") {
        this._spacing = value;
      }
    }
  },
  width: {
    get: function() {
      return this._width;
    },
    set: function(value) {
      if (Cesium.defined(value) && typeof value === "number") {
        this._width = value;
      }
    }
  },
  color: {
    get: function() {
      return this._color;
    },
    set: function(value) {
      if (Cesium.defined(value) && value instanceof Cesium.Color) {
        this._color = value;
      }
    }
  },
  granularity: {
    get: function() {
      return this._granularity;
    },
    set: function(value) {
      if (Cesium.defined(value) && typeof value === "number") {
        this._granularity = value;
      }
    }
  }
});
ContourLineAlgorithm.prototype.renderContourLines = function(contourLines, contourLineTable, options) {
  let appearance = options.appearance;
  if (!Cesium.defined(appearance)) {
    appearance = new Cesium.MaterialAppearance({
      material: Cesium.Material.fromType("Color", {
        color: this._color
      })
    });
  }
  const color = Cesium.defaultValue(options == null ? void 0 : options.color, Cesium.Color.RED);
  const width = Cesium.defaultValue(options == null ? void 0 : options.width, 2);
  const polylinePrimitiveList = contourLines.map((contourLine) => {
    return contourLine.createGroundPolylinePrimitive(appearance, {
      color,
      width
    });
  });
  if (Cesium.defined(options.collection) && options.collection instanceof Cesium.PrimitiveCollection) {
    polylinePrimitiveList.map(
      (contourLine) => options.collection.add(contourLine)
    );
  }
  return polylinePrimitiveList;
};
ContourLineAlgorithm.prototype._sampleTerrainMostDetailed = function(terrainProvider, cartographicList) {
  return new Promise(async (resolve, reject) => {
    try {
      if (Cesium == null ? void 0 : Cesium.when) {
        const promise = Cesium.sampleTerrainMostDetailed(
          terrainProvider,
          cartographicList
        );
        Cesium.when(promise, function(updatedPositions) {
          resolve(updatedPositions);
        });
      } else {
        const updatedPositions = await Cesium.sampleTerrainMostDetailed(
          terrainProvider,
          cartographicList
        );
        resolve(updatedPositions);
      }
    } catch (err) {
      reject(err);
    }
  });
};
ContourLineAlgorithm.prototype.doAlgorithm = function(positions) {
  return new Promise(async (resolve, reject) => {
    const terrainProviderName = this._viewer.terrainProvider.__proto__.constructor.name;
    if (terrainProviderName === "EllipsoidTerrainProvider") {
      reject("invalid TerrainProvider service,type is" + terrainProviderName);
    }
    try {
      const tessellation = this._doTessellation(positions);
      const cartesian3List = tessellation.positions;
      const cartographicList = cartesian3List.map(
        (cartesian3) => Cesium.Cartographic.fromCartesian(cartesian3, Cesium.Ellipsoid.WGS84)
      );
      const updatedCartographicList = await this._sampleTerrainMostDetailed(
        this._viewer.terrainProvider,
        cartographicList
      );
      const altitudeList = updatedCartographicList.map(
        (cartographic) => cartographic.height
      );
      const maxAltitude = arrayMaxValue(altitudeList), minAltitude = arrayMinValue(altitudeList);
      if (maxAltitude - minAltitude < this._spacing) {
        throw new Cesium.DeveloperError(
          "spacing is too large,no contour-line was generated!"
        );
      }
      const that = this;
      this._worker = new WorkerWrapper();
      this._worker.onmessage = function(event) {
        var _a, _b, _c;
        try {
          if (((_a = event == null ? void 0 : event.data) == null ? void 0 : _a.code) === 200) {
            const { tinSet, maxAltitude: maxAltitude2, minAltitude: minAltitude2 } = ((_b = event.data) == null ? void 0 : _b.data) || {};
            const result = that._createContourLineByTinList(
              tinSet,
              that._spacing,
              maxAltitude2,
              minAltitude2
            );
            resolve(result);
          } else {
            reject(((_c = event == null ? void 0 : event.data) == null ? void 0 : _c.msg) || "generate failed!");
          }
        } catch (error) {
          reject((error == null ? void 0 : error.message) || "generate failed!");
        } finally {
          that._worker.terminate();
          that._worker = null;
        }
      };
      this._worker.postMessage({
        //发送消息
        indieList: tessellation.indices,
        vertexList: tessellation.vertexes,
        altitudeList,
        maxAltitude,
        minAltitude
      });
    } catch (err) {
      reject((err == null ? void 0 : err.message) || "doAlgorithm failed!");
    }
  });
};
ContourLineAlgorithm.prototype.cancelAlgorithm = function() {
  if (this._worker) {
    this._worker.terminate();
  }
};
ContourLineAlgorithm.prototype._doTessellation = function(positions) {
  const polygon = Cesium.PolygonGeometry.fromPositions({
    positions,
    vertexFormat: Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
    //granularity越大,划分出来的三角网就越多
    granularity: this._granularity
  });
  const polygonGeometry = Cesium.PolygonGeometry.createGeometry(polygon);
  const indieList = polygonGeometry.indices;
  const vertexList = polygonGeometry.attributes.position.values;
  const positionList = [];
  for (let i = 0; i < indieList.length; i++) {
    const indieItem = indieList[i];
    const cartesian3 = new Cesium.Cartesian3(
      vertexList[indieItem * 3],
      vertexList[indieItem * 3 + 1],
      vertexList[indieItem * 3 + 2]
    );
    positionList.push(cartesian3);
  }
  return {
    positions: positionList,
    indices: indieList,
    vertexes: vertexList
  };
};
ContourLineAlgorithm.prototype._createContourLineByTinList = function(tinSet, spacing, maxAltitude, minAltitude) {
  const contourLineTable = this._getContourLineTable(
    maxAltitude,
    minAltitude,
    spacing
  );
  const contourLines = this._generateContourLines(tinSet, contourLineTable);
  return {
    contourLines,
    contourLineTable
  };
};
ContourLineAlgorithm.prototype._getContourLineTable = function(maxAltitude, minAltitude, spacing) {
  const diff = maxAltitude - minAltitude;
  if (diff <= spacing) {
    return [minAltitude];
  }
  const total = Math.floor(diff / spacing);
  const levels = [];
  for (let i = 0; i < total; i++) {
    levels.push(minAltitude + spacing * i);
  }
  return levels;
};
ContourLineAlgorithm.prototype._generateContourLines = function(tinSet, contourLineTable) {
  if (!Array.isArray(contourLineTable) || contourLineTable.length === 0)
    return [];
  const contourList = [];
  for (let i = 0; i < contourLineTable.length; i++)
    contourList.push(
      this._createContourLineByAltitude(tinSet, contourLineTable[i])
    );
  return contourList;
};
ContourLineAlgorithm.prototype._createContourLineByAltitude = function(tinSet, altitude) {
  const tinSetSize = tinSet.length;
  const linePositionsList = [];
  const contourLine = new ContourLine(altitude, linePositionsList);
  for (let i = 0; i < tinSetSize; i++) {
    const tinItem = tinSet[i];
    const curLinePositions = [];
    const startPointIndie = tinItem.vertexes[0];
    this._deepRetrievalTINItem(
      curLinePositions,
      tinItem,
      altitude,
      startPointIndie
    );
    if (curLinePositions.length > 0) {
      linePositionsList.push(curLinePositions);
    }
  }
  return contourLine;
};
ContourLineAlgorithm.prototype._deepRetrievalTINItem = function(linePositions, tinItem, height, startPointIndie) {
  const vertexes = tinItem.vertexes;
  const adjoinTINs = tinItem.adjointTINs;
  let lastTwoPointIdx = [];
  for (let k = 0; k < 3; k++) {
    const p1 = vertexes[k % 3], p2 = vertexes[(k + 1) % 3];
    if (isCrossLine(p1, p2, height)) {
      const p1WithHeight = transPoint2Cartesian(p1), p2WithHeight = transPoint2Cartesian(p2);
      const crossPoint = getMiddlePoint(p1WithHeight, p2WithHeight, height);
      linePositions.push(crossPoint);
      lastTwoPointIdx.splice(0, lastTwoPointIdx.length);
      lastTwoPointIdx.push(k % 3, (k + 1) % 3);
    }
  }
  tinItem.flag = height;
  if (lastTwoPointIdx.length === 0) {
    return linePositions;
  }
  const adjoinTinItemIdx = [0, 1, 2].filter(
    (idx) => lastTwoPointIdx.indexOf(idx) === -1
  )[0];
  if (!Cesium.defined(adjoinTinItemIdx) || adjoinTinItemIdx < 0) {
    return linePositions;
  }
  if (vertexes[adjoinTinItemIdx] === startPointIndie) {
    return linePositions;
  }
  const adjoinTinItem = adjoinTINs[adjoinTinItemIdx];
  if (!Cesium.defined(adjoinTinItem)) {
    return linePositions;
  }
  if (adjoinTinItem.flag === height) {
    return linePositions;
  }
  return this._deepRetrievalTINItem(
    linePositions,
    adjoinTinItem,
    height,
    startPointIndie
  );
};
function transPoint2Cartesian(point) {
  const cartesian3 = point.position;
  const cartographic = Cesium.Cartographic.fromCartesian(
    cartesian3,
    Cesium.Ellipsoid.WGS84
  );
  cartographic.height = point.w || 0;
  return Cesium.Cartographic.toCartesian(cartographic, Cesium.Ellipsoid.WGS84);
}
function isCrossLine(p1, p2, counterValue) {
  return (p1.w - counterValue) * (p2.w - counterValue) <= 0;
}
function getMiddlePoint(p1, p2, altitude) {
  const cartographicP1 = Cesium.Cartographic.fromCartesian(p1), cartographicP2 = Cesium.Cartographic.fromCartesian(p2);
  const lnglatP1 = {
    longitude: Cesium.Math.toDegrees(cartographicP1.longitude),
    latitude: Cesium.Math.toDegrees(cartographicP1.latitude),
    altitude: cartographicP1.height
  };
  const lnglatP2 = {
    longitude: Cesium.Math.toDegrees(cartographicP2.longitude),
    latitude: Cesium.Math.toDegrees(cartographicP2.latitude),
    altitude: cartographicP2.height
  };
  const startPt = lnglatP1.altitude < lnglatP2.altitude ? lnglatP1 : lnglatP2;
  const endPt = lnglatP1.altitude > lnglatP2.altitude ? lnglatP1 : lnglatP2;
  const denominator = (altitude - startPt.altitude) / (endPt.altitude - startPt.altitude);
  const midLnglat = {
    longitude: startPt.longitude + (endPt.longitude - startPt.longitude) * denominator,
    latitude: startPt.latitude + (endPt.latitude - startPt.latitude) * denominator,
    altitude
  };
  return Cesium.Cartesian3.fromDegrees(
    midLnglat.longitude,
    midLnglat.latitude,
    midLnglat.altitude
  );
}
export {
  ContourLineAlgorithm as default
};
